package shark.picture.service.impl;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONObject;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xddf.usermodel.chart.XDDFChartData;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSource;
import org.apache.poi.xddf.usermodel.chart.XDDFDataSourcesFactory;
import org.apache.poi.xddf.usermodel.chart.XDDFNumericalDataSource;
import org.apache.poi.xssf.usermodel.XSSFSheet;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.apache.poi.xwpf.usermodel.XWPFChart;
import org.apache.poi.xwpf.usermodel.XWPFDocument;
import org.apache.poi.xwpf.usermodel.XWPFParagraph;
import org.apache.poi.xwpf.usermodel.XWPFTable;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import shark.picture.common.BaseResponse;
import shark.picture.common.ResultUtils;
import shark.picture.data.entity.FileTemplate;
import shark.picture.exception.ErrorCode;
import shark.picture.service.FileTemplateService;
import shark.picture.service.UtilsService;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.*;

/**
 * @author jianxin.wei
 * @description
 * @date 2025/3/21 10:40
 */
@Service
@Slf4j
public class UtilsServiceImpl implements UtilsService {
    @Resource
    private FileTemplateService fileTemplateService;
    @Resource
    private StringRedisTemplate stringRedisTemplate;
    @Override
    public void base64ToImage(JSONObject jsonObject, HttpServletResponse response) throws IOException {
        String base64Data = jsonObject.getStr("base64Data");
        if (StrUtil.isBlank(base64Data)) {
            // 如果 Base64 数据为空，返回错误信息
            response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
            response.getWriter().write("Base64 数据为空");
            return;
        }
        // 解码 Base64 数据为字节数组
        byte[] imageBytes = Base64.decode(base64Data);

        // 获取图片的本身格式
        String imageType = getImageType(imageBytes);

        // 设置响应的内容类型为图片（这里假设是 PNG 格式）
        response.setContentType(imageType);
        response.setContentLength(imageBytes.length);

        // 将字节数组写入响应的输出流
        try (OutputStream out = response.getOutputStream()) {
            out.write(imageBytes);
            out.flush();
        } catch (IOException e) {
            response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
            response.getWriter().write("处理图片时发生错误,请检查输入的Base64编码是否正确完整。");
        }
    }

    @Override
    public BaseResponse<JSONObject> uploadTemplate(MultipartFile file) {
        try {
            String fileName = file.getOriginalFilename();
            assert fileName != null;
            String suffix = fileName.substring(fileName.lastIndexOf(".") + 1);
            fileName = fileName.substring(0, fileName.lastIndexOf("."));
            byte[] fileData = file.getBytes();
            QueryWrapper<FileTemplate> queryWrapper = new QueryWrapper<>();
            queryWrapper.eq("file_name", fileName).eq("file_suffix", suffix);
            FileTemplate fileTemplate = fileTemplateService.getBaseMapper().selectOne(queryWrapper);
            if (fileTemplate == null) {
                fileTemplate = new FileTemplate();
                fileTemplate.setFileName(fileName);
                fileTemplate.setFileSuffix(suffix);
            }
            fileTemplate.setFileData(fileData);
            fileTemplateService.saveOrUpdate(fileTemplate);

            JSONObject result = new JSONObject();
            XWPFDocument xwpfDocument = new XWPFDocument(new ByteArrayInputStream(fileData));
            List<XWPFTable> tables = xwpfDocument.getTables();
            List<XWPFChart> charts = xwpfDocument.getCharts();
            List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();
            List<Map<String, Object>> updateParagraphs = new ArrayList<>();
            for (int i = 0; i < paragraphs.size(); i++) {
                XWPFParagraph paragraph = paragraphs.get(i);
                String text = paragraph.getText();
                if (text.contains("{1}")) {
                    Map<String, Object> map = new HashMap<>();
                    map.put("text", text);
                    map.put("index", i);
                    updateParagraphs.add(map);
                }
            }

            result.set("tables", tables.size());
            result.set("charts", charts.size());
            result.set("updateParagraphs", updateParagraphs);

            return ResultUtils.success(result);
        } catch (Exception e) {
            log.info("上传失败", e);
            return (BaseResponse<JSONObject>) ResultUtils.error(ErrorCode.PARAMS_ERROR);
        }
    }

    @Override
    public BaseResponse<?> fillTemplate(JSONObject param) {
        try {
            String fileName = param.getStr("fileName");
            String fileSuffix = fileName.substring(fileName.lastIndexOf(".") + 1);
            fileName = fileName.substring(0, fileName.lastIndexOf("."));
            QueryWrapper<FileTemplate> queryWrapper = new QueryWrapper<FileTemplate>().eq("file_name", fileName).eq("file_suffix", fileSuffix);
            FileTemplate fileTemplate = fileTemplateService.getBaseMapper().selectOne(queryWrapper);
            JSONObject result = new JSONObject();
            XWPFDocument xwpfDocument = new XWPFDocument(new ByteArrayInputStream(fileTemplate.getFileData()));
            List<XWPFTable> tables = xwpfDocument.getTables();
            List<XWPFChart> charts = xwpfDocument.getCharts();
            List<XWPFParagraph> paragraphs = xwpfDocument.getParagraphs();

            // 图表 - 获取图表中需要的Excel数据 Map<图表坐标, Map<Excel中的行数 , List<插入Excel中的数据>> 并不固定式, 需要根据Excel数据动态生成,图表的关键是在替换时
            Map<Integer, Map<Integer , List<Object>>> chartMap = new HashMap<>();
            // 图表 - 替换 每个图表都不一样,类型差不多
            // 图表 - 都是先获取Excel表格页,和操作Excel一样,插入数据
            XWPFChart xwpfChart = charts.get(0);
            XSSFWorkbook workbook = xwpfChart.getWorkbook();
            XSSFSheet sheet = workbook.getSheetAt(0);
            sheet.getRow(0).getCell(1).setCellValue("单元格1");
            XDDFChartData chartData = xwpfChart.getChartSeries().get(0);
            XDDFChartData.Series series = chartData.getSeries().get(0);
            // 确定类型的范围
            XDDFDataSource cat = XDDFDataSourcesFactory.fromStringCellRange(sheet,
                    new CellRangeAddress(1, 3, 0, 0));
            // 确定对应类型的值 如果有多个值,再创建一个,val的size需要和cat的size一一对应
            XDDFNumericalDataSource val = XDDFDataSourcesFactory.fromNumericCellRange(sheet,
                    new CellRangeAddress(1, 3, 1, 1));
            series.replaceData(cat, val);
            xwpfChart.setTitleText("阶梯分布");
            xwpfChart.plot(chartData);


            // 段落 - 获取文本中需要替换的数据 Map<段落坐标, Map<替换标识, 替换内容>> 例如原语句"第一阶梯：{1}%",这里的替换标识为1;
            Map<Integer, Map<String, String>> textMap = new HashMap<>();
            Map<String, String> text1 = new HashMap<>();
            text1.put("1","'替换内容1'");
            Map<String, String> text3 = new HashMap<>();
            text3.put("1","'替换内容1'");
            text3.put("2","'替换内容2'");
            text3.put("3","'替换内容3'");
            text3.put("4","'替换内容4'");
            text3.put("5","'替换内容5'");
            text3.put("6","'替换内容6'");
            Map<String, String> text5 = new HashMap<>();
            text5.put("1","'替换内容1'");
            Map<String, String> text6 = new HashMap<>();
            text6.put("1","'替换内容1'");
            Map<String, String> text7 = new HashMap<>();
            text7.put("1","'替换内容1'");
            Map<String, String> text10 = new HashMap<>();
            text10.put("1","'替换内容1'");
            text10.put("2","'替换内容2'");
            text10.put("3","'替换内容3'");
            text10.put("4","'替换内容4'");
            text10.put("5","'替换内容5'");
            text10.put("6","'替换内容6'");

            textMap.put(1, text1);
            textMap.put(3, text3);
            textMap.put(5, text5);
            textMap.put(6, text6);
            textMap.put(7, text7);
            textMap.put(10, text10);

            // 段落 - 替换
            List<Map<String, Object>> updateParagraphs = new ArrayList<>();
            for (int i = 0; i < paragraphs.size(); i++) {
                XWPFParagraph paragraph = paragraphs.get(i);
                String text = paragraph.getText();
                if (text.contains("{1}")) {
                    Map<String, Object> map = new HashMap<>();


                    // 清除段落中的所有运行
                    while (!paragraph.getRuns().isEmpty()) {
                        paragraph.removeRun(0);
                    }
                    // 获取该段落对应的替换内容
                    Map<String, String> fillMap = textMap.get(i);
                    String newText = StrUtil.format(text, fillMap);
                    // 重新设置该段落文本
                    paragraph.createRun().setText(newText, 0);


                    map.put("text", text);
                    map.put("index", i);
                    map.put("newText", paragraph.getText());
                    updateParagraphs.add(map);
                }
            }

            result.set("tables", tables.size());
            result.set("charts", charts.size());
            result.set("updateParagraphs", updateParagraphs);
            return ResultUtils.success(result);
        } catch (Exception e) {
            log.info("替换内容失败", e);
            return ResultUtils.error(ErrorCode.OPERATION_ERROR);
        }
    }

    @Override
    public BaseResponse<?> imageToBase64(MultipartFile file) {
        try {
            byte[] bytes = file.getBytes();
            String encode = Base64.encode(bytes);
            return ResultUtils.success(encode);
        } catch (Exception e) {
            log.info("图片转换失败", e);
            return ResultUtils.error(ErrorCode.OPERATION_ERROR);
        }
    }


    /**
     * 获取图片格式
     */
    private String getImageType(byte[] imageBytes) {
        if (imageBytes == null || imageBytes.length < 4) {
            return null;
        }

        // 判断 PNG 格式
        if (imageBytes[0] == (byte) 0x89 && imageBytes[1] == (byte) 0x50 &&
                imageBytes[2] == (byte) 0x4E && imageBytes[3] == (byte) 0x47) {
            return "image/png";
        }

        // 判断 GIF 格式
        if (imageBytes[0] == (byte) 0x47 && imageBytes[1] == (byte) 0x49 &&
                imageBytes[2] == (byte) 0x46 && (imageBytes[3] == (byte) 0x38)) {
            return "image/gif";
        }

        // 判断 JPEG 格式
        if (imageBytes[0] == (byte) 0xFF && imageBytes[1] == (byte) 0xD8 &&
                imageBytes[2] == (byte) 0xFF) {
            return "image/jpeg";
        }

        return null;  // 未识别的格式
    }
}
